package es.uoc.mxpdroid.algorithm;

import java.util.ArrayList;


import es.uoc.mxpdroid.entity.XpGeoPoint;
import es.uoc.mxpdroid.entity.GridCell;
import es.uoc.mxpdroid.entity.RasterLayer;

public class LeastCostPathAlgorithm {

   private final static int   m_iOffsetX[]  = { 0, 1, 1, 1, 0, -1, -1, -1 };
   private final static int   m_iOffsetY[]  = { 1, 1, 0, -1, -1, -1, 0, 1 };

   public static final String GRAPH         = "GRAPH";
   public static final String PROFILEPOINTS = "PROFILEPOINTS";
   public static final String POINT         = "POINT";
   public static final String LAYERS        = "LAYERS";
   public static final String ACCCOST       = "ACCCOST";
   public static final String PROFILELINE   = "PROFILELINE";

   private RasterLayer      		m_Cost;
   private XpGeoPoint				m_Orig;
   private ArrayList<XpGeoPoint>	m_Path;
   //private RasterLayer				result;
   private ArrayList<float[]>		dd; //distance and level
   //private RasterLayer				m_Extras;
   private float					tDistance; //distance total

   
   public LeastCostPathAlgorithm(RasterLayer cost, XpGeoPoint origin){//, RasterLayer extra) {
	   m_Cost = cost;
	   m_Orig = origin;
	   m_Path = new ArrayList<XpGeoPoint>();
	   m_Path.add(origin);
	   //m_Extras = extra;
	   
	   dd = new ArrayList<float[]>();
	   tDistance = 0;
//	   result = new RasterLayer(cost.getLayerType());
//	   result.initFromLayer(cost);
//	   result.setNoDataValue(0);
//	   result.assignNoData();
	     
	   processAlgorithm();
   }
   
//   public RasterLayer getResultLayer(){
//	   return result;
//   }
   
   public ArrayList<XpGeoPoint> getPath(){
	   return m_Path;
   }
   
//   public float totalDistance(){
//	   float tmp = 0;
//	   for (int ii = 0; ii<dd.size(); ii++)
//		   tmp += dd.get(ii)[0];
//	   return tmp;
//   }
   
   public float totalDistance(){
	   return tDistance;
   }
   
//   public float maxDiffLevel(){
//	   float tmp_min = 999999;
//	   float tmp_max = -999999;
//	   for (int ii = 0; ii<dd.size(); ii++){
//		   if (dd.get(ii)[1] < tmp_min) tmp_min = dd.get(ii)[1];
//		   if (dd.get(ii)[1] > tmp_max) tmp_max = dd.get(ii)[1];
//	   }
//	   return tmp_max - tmp_min;
//   }
   
   private void processAlgorithm() {

      int iDirection;
      boolean bContinue = true;
      final GridCell cell = m_Orig.getGridCell(m_Cost);
      //float[] tmp;
      
      do {
         iDirection = getDirToNextDownslopeCell(cell.getX(), cell.getY());
         if (iDirection >= 0) {
        	//tmp = new float[2];
            cell.setX(cell.getX() + m_iOffsetX[iDirection]);
            cell.setY(cell.getY() + m_iOffsetY[iDirection]);
            m_Path.add( cell.getGeoPoint(m_Cost) );
            //result.setCellValue(cell.getX(), cell.getY(), 100);
            
            if (iDirection % 2 == 0)
            	tDistance += 15;
            else
            	tDistance += 15 * Math.sqrt(2);
            //tracking the distance and elevation of the path 
//            tmp[0] = (float)result.getDistToNeighborInDir(iDirection);
//            tmp[1] = 0;
//            if (m_Extras != null)
//            	tmp[1] = (float)m_Extras.getCellValueAsDouble(cell.getX(), cell.getY());
//            dd.add(tmp);
         }
         else {
            bContinue = false;
         }
      }
      while (bContinue );
   }

   private int getDirToNextDownslopeCell(final int x,
                                        final int y) {

      int i, iDir;
      double z, z2, dSlope, dMaxSlope;

      z = m_Cost.getCellValueAsDouble(x, y);

      if (m_Cost.isNoDataValue(z)) {
         return -1;
      }

      dMaxSlope = 0.0;
      for (iDir = -1, i = 0; i < 8; i++) {
         z2 = m_Cost.getCellValueAsDouble(x + m_iOffsetX[i], y + m_iOffsetY[i]);
         if (!m_Cost.isNoDataValue(z2)) {
            dSlope = (z - z2) / m_Cost.getDistToNeighborInDir(i);
            if (dSlope > dMaxSlope) {
               iDir = i;
               dMaxSlope = dSlope;
            }
         }
      }

      return iDir;

   }


}
